package common

import (
	"fmt"
	"strings"

	"github.com/shopspring/decimal"
)

// PrecisionConfig 精度配置
type PrecisionConfig struct {
	DefaultPrecision int32 `json:"default_precision"` // 默认精度（小数位数）
	MaxPrecision     int32 `json:"max_precision"`     // 最大精度
}

// GlobalPrecision 全局精度配置
var GlobalPrecision = &PrecisionConfig{
	DefaultPrecision: 2, // 默认2位小数
	MaxPrecision:     8, // 最大8位小数
}

// InitPrecision 初始化精度配置
func InitPrecision(config *PrecisionConfig) {
	if config != nil {
		GlobalPrecision = config
	}
}

// FormatDecimal 格式化decimal为字符串，保持精度
func FormatDecimal(d decimal.Decimal) string {
	return d.StringFixed(GlobalPrecision.DefaultPrecision)
}

// FormatDecimalWithPrecision 格式化decimal为指定精度的字符串
func FormatDecimalWithPrecision(d decimal.Decimal, precision int32) string {
	if precision < 0 {
		precision = GlobalPrecision.DefaultPrecision
	}
	if precision > GlobalPrecision.MaxPrecision {
		precision = GlobalPrecision.MaxPrecision
	}
	return d.StringFixed(precision)
}

// ParseDecimal 解析字符串为decimal，处理精度
func ParseDecimal(s string) (decimal.Decimal, error) {
	// 移除空白字符
	s = strings.TrimSpace(s)

	// 检查是否为空
	if s == "" {
		return decimal.Zero, nil
	}

	// 尝试解析为decimal
	d, err := decimal.NewFromString(s)
	if err != nil {
		return decimal.Zero, fmt.Errorf("invalid decimal format: %s", s)
	}

	// 限制精度
	return d.Round(GlobalPrecision.DefaultPrecision), nil
}

// ParseDecimalWithPrecision 解析字符串为decimal，指定精度
func ParseDecimalWithPrecision(s string, precision int32) (decimal.Decimal, error) {
	d, err := ParseDecimal(s)
	if err != nil {
		return decimal.Zero, err
	}

	if precision < 0 {
		precision = GlobalPrecision.DefaultPrecision
	}
	if precision > GlobalPrecision.MaxPrecision {
		precision = GlobalPrecision.MaxPrecision
	}

	return d.Round(precision), nil
}

// SafeAdd 安全的decimal加法，保持精度
func SafeAdd(a, b decimal.Decimal) decimal.Decimal {
	result := a.Add(b)
	return result.Round(GlobalPrecision.DefaultPrecision)
}

// SafeSub 安全的decimal减法，保持精度
func SafeSub(a, b decimal.Decimal) decimal.Decimal {
	result := a.Sub(b)
	return result.Round(GlobalPrecision.DefaultPrecision)
}

// SafeMul 安全的decimal乘法，保持精度
func SafeMul(a, b decimal.Decimal) decimal.Decimal {
	result := a.Mul(b)
	return result.Round(GlobalPrecision.DefaultPrecision)
}

// SafeDiv 安全的decimal除法，保持精度
func SafeDiv(a, b decimal.Decimal) (decimal.Decimal, error) {
	if b.IsZero() {
		return decimal.Zero, fmt.Errorf("division by zero")
	}
	result := a.Div(b)
	return result.Round(GlobalPrecision.DefaultPrecision), nil
}

// SafeDivWithPrecision 安全的decimal除法，指定精度
func SafeDivWithPrecision(a, b decimal.Decimal, precision int32) (decimal.Decimal, error) {
	if b.IsZero() {
		return decimal.Zero, fmt.Errorf("division by zero")
	}

	if precision < 0 {
		precision = GlobalPrecision.DefaultPrecision
	}
	if precision > GlobalPrecision.MaxPrecision {
		precision = GlobalPrecision.MaxPrecision
	}

	result := a.Div(b)
	return result.Round(precision), nil
}

// IsValidAmount 验证金额是否有效
func IsValidAmount(amount decimal.Decimal) bool {
	// 检查是否为负数
	if amount.LessThan(decimal.Zero) {
		return false
	}

	// 检查是否超过最大精度
	if amount.Exponent() < -GlobalPrecision.MaxPrecision {
		return false
	}

	return true
}

// ValidateAmountString 验证金额字符串
func ValidateAmountString(amountStr string) error {
	amount, err := ParseDecimal(amountStr)
	if err != nil {
		return err
	}

	if !IsValidAmount(amount) {
		return fmt.Errorf("invalid amount: %s", amountStr)
	}

	return nil
}

// RoundToCurrency 按货币类型四舍五入
func RoundToCurrency(amount decimal.Decimal, currency string) decimal.Decimal {
	precision := getCurrencyPrecision(currency)
	return amount.Round(precision)
}

// getCurrencyPrecision 获取货币精度
func getCurrencyPrecision(currency string) int32 {
	switch strings.ToUpper(currency) {
	case "USD", "EUR", "GBP", "CNY", "HKD", "SGD":
		return 2
	case "JPY":
		return 0
	case "BTC", "ETH":
		return 8
	default:
		return GlobalPrecision.DefaultPrecision
	}
}

// FormatCurrency 格式化货币金额
func FormatCurrency(amount decimal.Decimal, currency string) string {
	precision := getCurrencyPrecision(currency)
	return amount.StringFixed(precision)
}

// ParseCurrency 解析货币金额字符串
func ParseCurrency(amountStr, currency string) (decimal.Decimal, error) {
	precision := getCurrencyPrecision(currency)
	return ParseDecimalWithPrecision(amountStr, precision)
}

// CompareAmounts 比较两个金额
func CompareAmounts(a, b decimal.Decimal) int {
	return a.Cmp(b)
}

// IsAmountEqual 检查两个金额是否相等
func IsAmountEqual(a, b decimal.Decimal) bool {
	return a.Equal(b)
}

// IsAmountGreater 检查金额a是否大于金额b
func IsAmountGreater(a, b decimal.Decimal) bool {
	return a.GreaterThan(b)
}

// IsAmountLess 检查金额a是否小于金额b
func IsAmountLess(a, b decimal.Decimal) bool {
	return a.LessThan(b)
}

// IsAmountZero 检查金额是否为零
func IsAmountZero(amount decimal.Decimal) bool {
	return amount.IsZero()
}

// IsAmountPositive 检查金额是否为正数
func IsAmountPositive(amount decimal.Decimal) bool {
	return amount.GreaterThan(decimal.Zero)
}

// IsAmountNegative 检查金额是否为负数
func IsAmountNegative(amount decimal.Decimal) bool {
	return amount.LessThan(decimal.Zero)
}

// MinAmount 获取两个金额中的较小值
func MinAmount(a, b decimal.Decimal) decimal.Decimal {
	if a.LessThanOrEqual(b) {
		return a
	}
	return b
}

// MaxAmount 获取两个金额中的较大值
func MaxAmount(a, b decimal.Decimal) decimal.Decimal {
	if a.GreaterThanOrEqual(b) {
		return a
	}
	return b
}

// AbsAmount 获取金额的绝对值
func AbsAmount(amount decimal.Decimal) decimal.Decimal {
	return amount.Abs()
}

// Percentage 计算百分比
func Percentage(part, total decimal.Decimal) (decimal.Decimal, error) {
	if total.IsZero() {
		return decimal.Zero, fmt.Errorf("total cannot be zero")
	}

	result := part.Div(total).Mul(decimal.NewFromInt(100))
	return result.Round(2), nil
}

// CalculateFee 计算手续费
func CalculateFee(amount, rate decimal.Decimal) decimal.Decimal {
	fee := amount.Mul(rate).Div(decimal.NewFromInt(100))
	return fee.Round(GlobalPrecision.DefaultPrecision)
}

// CalculateAmountWithFee 计算包含手续费的金额
func CalculateAmountWithFee(amount, rate decimal.Decimal) decimal.Decimal {
	fee := CalculateFee(amount, rate)
	return SafeAdd(amount, fee)
}

// CalculateAmountWithoutFee 计算不包含手续费的金额
func CalculateAmountWithoutFee(totalAmount, rate decimal.Decimal) (decimal.Decimal, error) {
	if rate.IsZero() {
		return totalAmount, nil
	}

	// 公式：amount = totalAmount / (1 + rate/100)
	denominator := decimal.NewFromInt(1).Add(rate.Div(decimal.NewFromInt(100)))
	amount, err := SafeDiv(totalAmount, denominator)
	if err != nil {
		return decimal.Zero, err
	}

	return amount, nil
}

// ValidateAmountRange 验证金额范围
func ValidateAmountRange(amount, min, max decimal.Decimal) error {
	if amount.LessThan(min) {
		return fmt.Errorf("amount %s is less than minimum %s", amount.String(), min.String())
	}

	if amount.GreaterThan(max) {
		return fmt.Errorf("amount %s is greater than maximum %s", amount.String(), max.String())
	}

	return nil
}

// StringToDecimal 字符串转decimal的安全方法
func StringToDecimal(s string) decimal.Decimal {
	if s == "" {
		return decimal.Zero
	}

	d, err := decimal.NewFromString(strings.TrimSpace(s))
	if err != nil {
		return decimal.Zero
	}

	return d.Round(GlobalPrecision.DefaultPrecision)
}

// Float64ToDecimal float64转decimal的安全方法
func Float64ToDecimal(f float64) decimal.Decimal {
	return decimal.NewFromFloat(f).Round(GlobalPrecision.DefaultPrecision)
}

// DecimalToFloat64 decimal转float64的安全方法（注意：可能损失精度）
func DecimalToFloat64(d decimal.Decimal) float64 {
	f, _ := d.Float64()
	return f
}

// DecimalToString decimal转字符串的安全方法
func DecimalToString(d decimal.Decimal) string {
	return FormatDecimal(d)
}

// SumAmounts 安全地求和多个金额
func SumAmounts(amounts ...decimal.Decimal) decimal.Decimal {
	total := decimal.Zero
	for _, amount := range amounts {
		total = SafeAdd(total, amount)
	}
	return total
}

// AverageAmounts 计算多个金额的平均值
func AverageAmounts(amounts ...decimal.Decimal) decimal.Decimal {
	if len(amounts) == 0 {
		return decimal.Zero
	}

	total := SumAmounts(amounts...)
	count := decimal.NewFromInt(int64(len(amounts)))

	result, err := SafeDiv(total, count)
	if err != nil {
		return decimal.Zero
	}

	return result
}
